/**
 * Element.Visibility.js
 *
 * Extends the basic Element Class with additional methods for show, hide, and toggle elements via display style.
 *
 * @license:		MIT-style license
 * @author:		Alex Yaroshevich
 * @copyright		Authors
 */
Element.implement({
	visible: function() { return this.style.display != 'none'; },
	toggle: function() { return (this.visible())?this.hide():this.show(); },
	hide: function() { this.style.display='none'; return this; },
	show: function() { this.style.display=''; return this; }
});


/**
 * Element.AJAX.js
 * 
 * Extends the basic Element Class with additional methods for inline ajax requesting.
 * 
 * @license:		MIT-style license
 * @author:		Alex Yaroshevich
 * @copyright		Authors
 */
Element.implement({
	touch: function( _options ) {
		var tag = this.get('tag');
		if(tag!='a') return null;
		
		_options = _options || {};
		_options.data = _options.data || '';
		if( $type( _options.data ) == 'object' )
			_options.data = (new Hash(_options.data||{})).toQueryString();
		
		var url = this.get('href');
		var options = new Hash({
			'method': 'post',
			'headers': { 'Accept': 'application/json', 'X-Request': 'a.touch' },
			'url': url,
			'async': false,
			'noCache': true
		});
		
		options.extend( _options );
		
		//console.debug( 'options', options.getClean() );
		
		this.set( 'send', options.getClean() );
		
		var r = this.get( 'send' );
		//console.debug( r );
		r.send( );
		
		if( !r.response.text ) return null;
		
		r.response.json = JSON.decode( r.response.text );
		if( r.response.json.out ) alert( r.response.json.out );
		return r.response.json.result;
	}
});


/**
 * Fx.ProgressBar
 *
 * @version		1.0
 * @license		MIT License
 * @author		Harald Kirschner <mail [at] digitarald [dot] de>
 * @copyright		Authors
 */

Fx.ProgressBar = new Class({

	Extends: Fx,

	options: {
		text: null,
		transition: Fx.Transitions.Circ.easeOut,
		link: 'cancel'
	},

	initialize: function(element, options) {
		this.element = $(element);
		this.parent(options);
		this.text = $(this.options.text);
		this.set(0);
	},

	start: function(to, total) {
		return this.parent(this.now, (arguments.length == 1) ? to.limit(0, 100) : to / total * 100);
	},

	set: function(to) {
		this.now = to;
		this.element.setStyle('backgroundPosition', (100 - to) + '% 0px');
		if (this.text) this.text.set('text', Math.round(to) + '%');
		return this;
	}

});


// myhash5 hashing algorithm analog (like md5, but very simply)
$mh5=function(v,q){
	if(!q)q=true;//64bits result
	var cc=function(_c){return (_c&&_c[0])?_c.charCodeAt(0)%127:0;};
	var shl=function(n,s){n=parseInt(n);return (s<=24)?(n<<s):((n<<24)*Math.pow(2,s-24));};
	var len=v.length;v+=' '+String.fromCharCode(cc(v[0])^0x39);v+=' '+String.fromCharCode(cc(v[2])^0x52);
	var tr=[];for(var i=0,l=v.length;i<l;i+=3){var b=0;for(var j=0;j<3;j++)b|=shl(cc(v[i+j]),7*j);var m=(b&0x7e7e0)>>9,n=(b&0xbcbc8)>>7;b&=(0xffff^((b>>16)^0xff));tr[i%3]^=b^(~m);tr[(i+1)%3]^=-b^(~n);tr[(i+2)%3]^=-b^(n|m);}
	for(var i=0;i<3;i++) tr[i]=((tr[i]+65536));
	var b64="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_";
	var r=""; for(var i=0;i<3;i++) r+=b64[(tr[i]>>12)&0x3f]+b64[(tr[i]>>6)&0x3f]+b64[tr[i]&0x3f];
	len%=4096;r=r.substr(0,5)+(b64[(len>>6)&0x3f]+b64[len&0x3f])+r.substr(5);
	return r;
}


/**
 * FormValidator addon contains fixedLength, money
 *
 * @version		0.9
 * @license		MIT License
 * @author		Alex Yaroshevich <qfox [at] ya [dot] ru>
 * @copyright		Authors
 */
if( FormValidator )
{
FormValidator.add('IsEmpty', {

	errorMsg: false,
	test: function(element){
		if (element.type == 'select-one' || element.type == 'select')
			return !(element.selectedIndex >= 0 && element.options[element.selectedIndex].value != '');
		else
			return ((element.get('value') == null) || (element.get('value').length == 0));
	}

});
FormValidator.addAllThese([
	['fixedLength', {
		errorMsg: function(element, props){
			var value = element.get('value'); if (Browser.Engine.trident5) { value = value.replace(/[_\-\s]/g,''); } // fucking ie
			
			if ($type(props.fixedLength))
				return FormValidator.getMsg('fixedLength').substitute({fixedLength:props.fixedLength,length:value.length });
			else return '';
		},
		test: function(element, props){
			if (!$type(props.fixedLength) || element.get('value').length == 0) return true;
			var value = element.get('value'); if (Browser.Engine.trident5) { value = value.replace(/[_\-\s]/g,''); } // fucking ie
			return (value.length == $pick(props.fixedLength,10));
		}
	}],
	
	['validate-money', {
		errorMsg: FormValidator.getMsg.pass('money'),
		test: function(element){
			return FormValidator.getValidator('IsEmpty').test(element) || (/^(,?[0-9]{1,3})+\.[0-9]{2}$/).test(element.get('value'));
		}
	}]
]);

MooTools.lang.set('en-US', 'FormValidator', {
	fixedLength:'Please enter equal {fixedLength} characters (you entered {length} characters).',
	money:'Please enter a valid amount of money such as XXX.XXX,YY. i.e. "4.120.122.000,14".'
});

/**
 * Script: FormValidator.Russian.js
 *
 * FormValidator messages for Russian
 * 
 * @version		0.9
 * @license		MIT License
 * @author		Alex Yaroshevich <qfox [at] ya [dot] ru>
 * @copyright		Authors
 */

MooTools.lang.set('ru-RU-unicode', 'FormValidator', {

	required:'Поле обязательно для заполнения.',
	minLength:'Пожалуйста введите не меньше {minLength} символов (вы ввели {length}).',
	maxLength:'Пожалуйста введите не больше {maxLength} символов (вы ввели {length}).',
	integer:'Пожалуйста введите целое число в это поле. Числа с плавающей точкой (например, 3.14) не разрешены.',
	numeric:'Пожалуйста введите численное значение в это поле (в т.ч. "1", "3.14", "-1", "-3.14").',
	digits:'Пожалуйста используйте только числа и знаки препинания в этом поле (например, телефонный номер с черточками и точками).',
	alpha:'Пожалуйста используйте только буквы (a-z) в этом поле. Не разрешено ни пробелов, ни других символов.',
	alphanum:'Пожалуйста используйте только буквы (a-z) или цифры (0-9) в этом поле. Не разрешено ни пробелов, ни других символов.',
	dateSuchAs:'Пожалуйста введите дату в разрешенном формате: {date}',
	dateInFormatMDY:'Пожалуйста введите дату в американском формате ММ/ДД/ГГГГ (например, "12/31/1999" - 31 декабря 1999 года)',
	email:'Пожалуйста введите email-адрес. Например, "ivan.petrov@ya.ru".',
	url:'Пожалуйста введите интернет-адрес. Например, "http://qfox.ru/".',
	currencyDollar:'Пожалуйста введите сумму в долларах ($). Например "$100.00".',
	oneRequired:'Пожалуйста введите данные в одно из этих полей.',
	errorPrefix: 'Ошибка: ',
	warningPrefix: 'Внимание! ',
	
	//FormValidator.Extras
	
	noSpace: 'В этом поле не должно быть пробелов.',
	reqChkByNode: 'Ничего не выбрано.',
	requiredChk: 'Поле обязательно.',
	reqChkByName: 'Пожалуйста выберите {label}.',
	match: 'Это поле должно совпадать с полем {matchName}.',
	startDate: 'начальная дата',
	endDate: 'конечная дата',
	currendDate: 'текущая дата',
	afterDate: 'Дата должна быть либо сегодняшним днем либо позже {label}.',
	beforeDate: 'Дата должна быть либо сегодняшним днем либо ранее {label}.',
	startMonth: 'Пожалуйста выберите начальный месяц',
	sameMonth: 'Эти две даты должы быть в одном месяце - Вы должны изменить одну из них.',
	
	fixedLength:'Должно быть {fixedLength} символов. Вы ввели {length}.',
	money:'Поле должно быть заполнено в денежном формате XXX.XXX,YY. Например, "4.120.122.000,14".'
});

}


/**
 * Date.format patch
 *
 * @version		0.9
 * @license		MIT License
 * @author		Alex Yaroshevich <qfox [at] ya [dot] ru>
 * @copyright		Authors
 */
if( Date.parsePatterns ) {
Date.parsePatterns.unshift( {
	//"31.12.08", "31.12.2008", "5.5.08"
	re: /^(\d{1,2})[\.](\d{1,2})[\.](\d{2,4})$/,
	handler: function(bits){
		return new Date(bits[3], bits[2] - 1, bits[1]);
	}
} );
}



/**
 * Sleep
 *
 */
function $sleep(milliseconds) {
	var start = $time();
	while( ($time() - start) < milliseconds ) {}
}

/**
 * range function
 *
 */
function $range(a,b,s) {
	var r = $A([]), from = Math.min(a,b), to = Math.max(a,b), s = Math.abs(s||1);
	for(var i=from;i<=to;i+=s) r.push(i);
	return r;
}

/**
 * Date patch
 *
 */
(function(){

var pad = function(what, length, char){
	if( length < String(what).length ) return what;
	return new Array(length - String(what).length + 1).join(char||'0') + what;
};

Date.implement('__format', Date.prototype.format);
delete Date.prototype.format;

Date.implement({
	setDay: function(day){
		day = parseInt(day||0)%7;
		this.increment('day',(7-this.get('day')+day)%7);
		return this;
	},
	getFirstSunday: function(year){
		year = $pick( year, this.get('year') );
		return (new Date()).set({'year':year,'mo':0,'date':1,'day':0}); // lang safe vs Date.parse('next sunday')
	},
	getFirstMonday: function(year){
		year = $pick( year, this.get('year') );
		return (new Date()).set({'year':year,'mo':0,'date':1,'day':1}); // lang safe vs Date.parse('next monday')
	},
	
	getUnix: function(){
		return (Date.UTC(this.get('year'),this.get('mo'),this.get('date'),
			this.get('UTCHours'), this.get('UTCMinutes'), this.get('UTCSeconds'))/Date.units.second()).floor();
	},
	
	getISOYear: function(){
		var year = parseInt(this.get('year')), dayOfYear = parseInt(this.get('dayofyear')), weekDay = this.getISODay( );
		var jan1wd = (new Date()).set({'year':year,'mo':0,'date':1}).getISODay( ); //jan1WeekDay
		
		if( 4 < jan1wd && jan1wd <= 8 - dayOfYear )
			year --;
		else if( (this.isLeapYear() ? 366 : 365) < dayOfYear + 4 - weekDay )
			year ++;
                
		return year;
	},
	getISOWeek: function(){
		var year = parseInt(this.get('year')), dayOfYear = parseInt(this.get('dayofyear')), weekDay = this.getISODay( );
		var jan1wd = (new Date()).set({'year':year,'mo':0,'date':1}).getISODay( ); // jan1WeekDay
		
		var week = 52;
		if( 4 < jan1wd && jan1wd <= 8 - dayOfYear )
			week = ( jan1wd == 5 || (jan1wd == 6 && this.clone().decrement('year').isLeapYear()) ) ? 53 : 52;
		else if( (this.isLeapYear() ? 366 : 365) < dayOfYear + 4 - weekDay )
			week = 1;
		else
			week = ( (dayOfYear + (7-weekDay) + (jan1wd-1)) / 7 ).floor() + ( jan1wd > 4 ? -1 : 0 );
                
		return week;
	},
	getISODay: function(){
		return (parseInt(this.get('day'))+6)%7+1;
	},
	format: function(f){
		if (!this.isValid()) return 'invalid date';
		
		if( $type( f ) == 'string' )
		{
		var d = this;
		f = f.replace(/%([a-z])/gi,
			function($0, $1){
				switch ($1){
					case 'e': return d.get('date');
					case 'g': return pad( d.getISOYear( ), 4).substr(2);
					case 'G': return pad( d.getISOYear( ), 4);
					case 'l': return pad( d.get('hour'), 2, ' ' );
					case 'P': return String( Date.getMsg( d.get('ampm') ) ).toLowerCase( );
					case 'r': return d.format("%I:%M:%S %p");
					case 'R': return d.format("%H:%M");
					case 'u': return d.getISODay( );
					case 'U': return pad( ((1+d.get('dayofyear')-d.getFirstSunday().get('dayofyear'))/7).ceil(), 2 );
					case 'V': return pad( d.getISOWeek( ), 2 );
					case 'W': return pad( ((1+d.get('dayofyear')-d.getFirstMonday().get('dayofyear'))/7).ceil(), 2 );
					case 's': return d.getUnix();
					case 'n': return "\n";
					case 't': return "\t";
				}
				return $0;
			}
		);
		arguments[0] = f;
		}
		
		return this.__format.apply(this, arguments);
	}
});

})();


/**
 * String patch for russian letters
 */
delete String.prototype.hyphenate;
delete String.prototype.capitalize;
String.implement({
	hyphenate: function(){
		return this.replace(/[A-ZА-Я]/g, function(match){
			return ('-' + match.charAt(0).toLowerCase());
		});
	},

	capitalize: function(){
		return this.replace(/([^а-яА-Яa-zA-Z0-9_]|^)([a-zа-я])/g, function(match){
			return match.toUpperCase();
		});
	}
});



/**
 * Splash screen)
 */
window.splash = { };
window.splash.init = function( cls ) {
	this._cls = ''+(cls||'');
};
window.splash.visible = function( cls ) {
	cls = '.'+(cls || this._cls);
	return $$('div.splash'+cls).length > 0;
};
window.splash.show = function( cls ) {
	cls = ''+(cls || this._cls);
	if( !this.visible( cls ) )
		(new Element('div',{'class':'splash '+cls+' big'})).inject($$('body')[0]);
};
window.splash.hide = function( cls ) {
	cls = '.'+(cls || this._cls);
	$$('div.splash'+cls).destroy();
};


/**
 * Event.Keys patch
 *
 */
(function(){
Event.Keys.extend({
	'multiply': 106,
	'add': 107,
	'plus': 107,
	'separator': 108,
	'subtract': 109,
	'minus': 109,
	'divide': 111
});
})();